package com.maidou.qiantai.common.utils.jwt;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.SneakyThrows;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * @Author maidou
 * @Date 2023/4/26 13:03
 */
@Component
public class JwtUtils {
    //签名秘钥
    private static final String secret = "159357";

    //过期时间
    //认证token  1小时
    public static final Long accessTokenExpiration = 60 * 60 * 24 * 30L;
    //刷新token 1个月
    public static final Long refreshTokenExpiration = 60 * 60 * 24 * 30L;

    /**
     * 生成 access-token 方法，提供给外部使用
     *
     * @param
     * @return
     */
    public static String generateAccessToken(Long id) {
        return generateToken(id, accessTokenExpiration);
    }

    /**
     * 生成 refresh-token 方法，提供给外部使用
     *
     * @param
     * @return
     */
    public static String generateRefreshToken(Long id) {
//        TokenTO tokenTO = new TokenTO();
//        tokenTO.setId(id);
        return generateToken(id, refreshTokenExpiration);
    }

    /**
     * 生成 token 方法
     *
     * @param id  用户对象
     * @param expiration 过期时间
     * @return
     */
    private static String generateToken(Long id, Long expiration) {
        HashMap<String,Object> claims = new HashMap<>();
        String s = JSON.toJSONString(id);
//        HashMap<String, Object> claims = JSON.parseObject(s, new TypeReference<HashMap<String, Object>>() {
//        });
        return createToken(claims, s, expiration);
    }

    /**
     * 创建 token 不提供给外部使用，只能本类使用
     *
     * @param claims    对象转为map
     * @param subject   该项目token的名称，根据该名称判断是否为本项目的生成的token
     * @param expiration 过期时间
     * @return
     */
    private static String createToken(Map<String, Object> claims, String subject, Long expiration) {
        Date now = new Date();
        Date expirationDate = new Date(now.getTime() + expiration * 1000);
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(now)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
    /**
     * 这段代码是用于验证JWT token是否有效的
     * @param token 生成的token
     * @return
     */
    @SneakyThrows
    public static Boolean validateToken(String token) {
//        String userid = extractUsername(token);
        return !isTokenExpired(token);
    }

    /**
     * 这段代码是用于从JWT token中提取用户名（Subject）的
     * 用户名（Subject）是一个声明（Claim），它指定了JWT token所代表的实体的标识符。
     * @param token 生成的token
     * @return
     */
    public static String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    /**
     * 这段代码是用于从JWT token中提取过期时间的
     * @param token 生成的token
     * @return
     */
    public static Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    /**
     * 使用claimsResolver函数式接口提供的方法，从Claims对象中提取特定的声明信息并返回。
     * @param token 生成的token
     * @param claimsResolver 函数式接口
     * @param <T>
     * @return
     */
    public static  <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    /**
     * 解析JWT token并提取其中的所有声明（Claims）信息（secret根据此秘钥）
     * @param token
     * @return
     */
    private static Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }

    /**
     * 判断token是否过期，主要逻辑是 过期时间与当前时间比较
     * @param token
     * @return
     */
    private static Boolean isTokenExpired(String token) {
        final Date expiration = extractExpiration(token);
        return expiration.before(new Date());
    }
}